package com.ht.WeNas.config.web;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.ht.WeNas.data.dto.UserDto;
import com.ht.WeNas.data.vo.Res;
import com.ht.WeNas.utils.ThreadLocalUtil;
import com.ht.WeNas.utils.TokenUtil;
import com.ht.WeNas.utils.tag.PublicApi;
import com.ht.WeNas.utils.tag.RequireUserType;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/**
 * 基础拦截器，拦截基础用户
 */
public class BasicInterceptor implements HandlerInterceptor {
    public void sendErrorRes(HttpServletResponse response, String msg, int httpStatus) throws IOException {
        response.setStatus(httpStatus);
        response.setContentType(ContentType.JSON.getValue());
        try (
                OutputStream os = response.getOutputStream()
        ) {
            os.write(JSONUtil.toJsonStr(Res.fail(msg)).getBytes(StandardCharsets.UTF_8));
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod handlerMethod) {
            boolean isPublic = this.processPublicApi(handlerMethod);
            if (isPublic) {
                return true;
            }
            return this.processRequiredUserType(request, response, handlerMethod);
        }
        return true; // 允许请求继续
    }

    /**
     * 处理 PublicApi 注解。
     * <p>被注解标记的控制器直接放行，无需用户登录</p>
     */
    private boolean processPublicApi(HandlerMethod handlerMethod) throws IOException {
        PublicApi publicApi = handlerMethod.getMethodAnnotation(PublicApi.class);
        return publicApi != null;
    }
    
    /**
     * 处理 RequiredUserType 注解。
     * <p>被注解标记的控制器内部方法需要使用用户信息，需要获取设置用户信息</p>
     * <p>如果方法被 PublicApi 注解注释，则不执行该方法</p>
     */
    private boolean processRequiredUserType(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws IOException {
        RequireUserType annotation = handlerMethod.getMethodAnnotation(RequireUserType.class);
        int userLevel;
        if (annotation == null || annotation.value() == null) {
            userLevel = -1;
        } else {
            userLevel = annotation.value().getValue();
        }

        // 当前接口不公开，需要通过 jwt 获取用户信息
        Cookie[] cookies = request.getCookies();
        String jwtToken = null;
        if (cookies == null) {
            // 如果没有Token，则拒绝访问
            sendErrorRes(response, "未登录，禁止访问", HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        for (var cookie : cookies) {
            // 从 cookie 中获取token
            if ("token".equals(cookie.getName())) {
                jwtToken = cookie.getValue();
            }
        }
        if (StrUtil.isBlank(jwtToken)) {
            // 如果没有Token，则拒绝访问
            sendErrorRes(response, "未登录，禁止访问", HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        try {
            UserDto UserDto = TokenUtil.getUser(jwtToken);
            if (UserDto == null || StrUtil.isBlankIfStr(UserDto.getId())) {
                throw new RuntimeException();
            }
            // 判断当前用户是否有访问资格
            if (UserDto.getUserType() != null && UserDto.getUserType().getValue() >= userLevel) {
                ThreadLocalUtil.setUserId(Long.parseLong(UserDto.getId()));
                ThreadLocalUtil.setUserDto(UserDto);
                return true;
            } else {
                sendErrorRes(response, "权限不足，拒绝访问", HttpServletResponse.SC_NOT_ACCEPTABLE);
                return false;
            }
        } catch (RuntimeException exception) {
            // token 解析错误，拒绝访问
            sendErrorRes(response, "token错误，拒绝访问，请重新登录", HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        response.setCharacterEncoding("utf-8");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清除 ThreadLocal 中的数据，防止内存泄漏
        ThreadLocalUtil.removeAll();
    }
}
